﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;

namespace Xceed.Silverlight.Data.Stats
{
  public class RootMeanSquareFunction : CumulativeStatFunction
  {
    public RootMeanSquareFunction()
      : base()
    {
    }

    public RootMeanSquareFunction( string resultPropertyName, string sourcePropertyName )
      : base( resultPropertyName, sourcePropertyName )
    {
    }

    protected internal override void Validate()
    {
      base.Validate();
      this.ValidateSourcePropertyName( 1 );
    }

    protected override void Initialize( Type[] sourcePropertyTypes )
    {
      base.Initialize( sourcePropertyTypes );

      m_accumulationType = StatFunction.GetDefaultNumericalAccumulationType( sourcePropertyTypes[ 0 ] );
    }

    protected internal override void Reset()
    {
      m_longSum = 0;
      m_doubleSum = 0d;
      m_decimalSum = 0m;
      m_count = 0;
    }

    protected internal override void Accumulate( object[] values )
    {
      if( StatFunction.CanProcessValues( values, 1 ) )
      {
        checked
        {
          switch( m_accumulationType )
          {
            case TypeCode.Int64:
              {
                long number = Convert.ToInt64( values[ 0 ] );
                m_longSum += number * number;
                m_count++;
              }
              break;

            case TypeCode.Double:
              {
                double number = Convert.ToDouble( values[ 0 ] );
                m_doubleSum += number * number;
                m_count++;
              }
              break;

            case TypeCode.Decimal:
              {
                Decimal number = Convert.ToDecimal( values[ 0 ] );
                m_decimalSum += number * number;
                m_count++;
              }
              break;
          }
        }
      }
    }

    protected internal override void AccumulateChildResult( StatResult childResult )
    {
      RootMeanSquareResult rmsResult = ( RootMeanSquareResult )childResult;

      checked
      {
        switch( m_accumulationType )
        {
          case TypeCode.Int64:
            m_longSum += Convert.ToInt64( rmsResult.Sum );
            m_count += rmsResult.Count;
            break;

          case TypeCode.Double:
            m_doubleSum += Convert.ToDouble( rmsResult.Sum );
            m_count += rmsResult.Count;
            break;

          case TypeCode.Decimal:
            m_decimalSum += Convert.ToDecimal( rmsResult.Sum );
            m_count += rmsResult.Count;
            break;
        }
      }
    }

    protected internal override StatResult GetResult()
    {
      object result = null;
      object sum = null;

      if( m_count == 0 )
        throw new DivideByZeroException();

      checked
      {
        switch( m_accumulationType )
        {
          case TypeCode.Int64:
            sum = m_longSum;
            result = Math.Sqrt( Convert.ToDouble( m_longSum ) / m_count );
            break;

          case TypeCode.Double:
            sum = m_doubleSum;
            result = Math.Sqrt( m_doubleSum / m_count );
            break;

          case TypeCode.Decimal:
            sum = m_decimalSum;
            result = Math.Sqrt( Convert.ToDouble( m_decimalSum ) / m_count );
            break;
        }
      }

      return new RootMeanSquareResult( result, sum, m_count );
    }

    private TypeCode m_accumulationType = TypeCode.Empty;
    private long m_longSum = 0;
    private double m_doubleSum = 0d;
    private decimal m_decimalSum = 0m;
    private long m_count = 0;

    private class RootMeanSquareResult : StatResult
    {
      public RootMeanSquareResult( object value, object sum, long count )
        : base( value )
      {
        m_sum = sum;
        m_count = count;
      }

      #region Sum Property

      public object Sum
      {
        get
        {
          return m_sum;
        }
      }

      private object m_sum;

      #endregion

      #region Count Property

      public long Count
      {
        get
        {
          return m_count;
        }
      }

      private long m_count;

      #endregion
    }
  }
}
